package com.yzs.core.base

import androidx.lifecycle.MutableLiveData
import com.ykwl.service.http.RetrofitManager
import com.yzs.core.bean.NetworkViewState
import com.yzs.core.bean.PageStatus
import com.yzs.core.http.ApiException
import com.yzs.core.http.ApiService
import com.yzs.core.utils.DateUtil
import kotlinx.coroutines.*
import org.json.JSONException
import retrofit2.HttpException
import java.lang.reflect.Modifier
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
import java.util.*
import kotlin.collections.HashMap

/**
 *    author : yzs
 *    date   : 2022/1/12 11:50
 *    desc   :
 */

typealias ApiError = suspend (e: ApiException) -> Unit


open class BaseRepository(
    protected val coroutineScope: CoroutineScope,
    protected val viewStates: MutableLiveData<NetworkViewState>
) {

    protected val api by lazy { RetrofitManager.getApiService(ApiService::class.java) }

    protected fun <T> launch(
        block: suspend () -> T,
        success: suspend (T) -> Unit,
        error: ApiError? = null
    ): Job {
        viewStates.postValue(NetworkViewState("加载中...", PageStatus.LOADING))
        return coroutineScope.launch {
            runCatching {
                withContext(Dispatchers.IO) {
                    block()
                }
            }.onSuccess {
                runCatching {
                    success(it)
                    viewStates.value = NetworkViewState("加载成功", PageStatus.LOADING)
                }.onFailure {
                    it.printStackTrace()
                    val e = ApiException("数据转换异常", -1)
                    error?.invoke(e)
                    viewStates.value = NetworkViewState("数据转换异常", PageStatus.FAILURE(e))
                }
            }.onFailure {
                it.printStackTrace()
                getApiException(it).apply {
                    error?.invoke(this)
                    // 可以做统一提示处理
                    // Toast
                    // 统一响应错误信息
                    viewStates.value = NetworkViewState(this.errorMessage, PageStatus.FAILURE(this))
                }
            }
        }
    }


    /**
     * 捕获异常信息
     */
    private fun getApiException(e: Throwable): ApiException {
        val apiException = when (e) {
            is UnknownHostException -> {
                ApiException("网络异常，请检查网络", -100)
            }
            is SocketException -> {
                ApiException("网络异常，请检查网络手机是否可以上网", -100)
            }
            is JSONException -> {
                ApiException("数据异常", -100)
            }
            is SocketTimeoutException -> {
                ApiException("连接超时", -100)
            }
            is ConnectException -> {
                ApiException("无法连接服务器", -100)
            }
            is HttpException -> {
                ApiException(e.message(), e.code())
            }
            is ApiException -> {
                e
            }
            /**
             * 如果协程还在运行，个别机型退出当前界面时，viewModel会通过抛出CancellationException，
             * 强行结束协程，与java中InterruptException类似，所以不必理会,只需将toast隐藏即可
             */
            is CancellationException -> {
                ApiException("", -10)
            }
            else -> {
                ApiException("未知错误", -100)
            }
        }
        return apiException
    }


    /**
     * 转 String 添加分隔符
     *
     * @param list      集合
     * @param separator 分隔符
     * @return
     */
    protected open fun listToString(list: List<Any>?, separator: String): String {
        if (list == null) return ""
        val sb = StringBuilder()
        for (i in list.indices) {
            if (i == list.size - 1) {
                sb.append(list[i])
            } else {
                sb.append(list[i])
                sb.append(separator)
            }
        }
        return sb.toString()
    }


    /**
     * 将实体中的属性和值，转化为map的key和value
     * 当属性的值为空时，不加入map
     * 当属性为Date类型时，转化为 yyyy-MM-dd HH:mm:ss 格式的字符串
     *
     * @param obj 实体对象
     * @return map
     */
    protected open fun toMap(obj: Any?): Map<String, String> {
        val map: MutableMap<String, String> = HashMap()
        if (obj != null) {
            // 获取对象对应类中的所有属性域
            val fields = obj.javaClass.declaredFields
            for (field in fields) {
                if (field.modifiers == Modifier.PRIVATE) {
                    //获得对象的私有属性名称
                    val fieldName = field.name
                    //获取原来的访问控制权限
                    val accessFlag = field.isAccessible
                    field.isAccessible = true
                    // 获取在对象中属性对应的对象中的变量
                    try {
                        var o = field[obj]
                        if (o != null) {
                            if (o is Date) {
                                o = DateUtil.getString(o)
                            }
                            map[fieldName] = o.toString()
                        }
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                    // 恢复访问控制权限
                    field.isAccessible = accessFlag
                }
            }
        }
        return map
    }
}